Cplex callback

q```

Generic callbacks in Cplex


// install a "lazyconstraint" callback to cut infeasible integer sol.s (found e.g. by heuristics)
CPXLONG contextid = CPX_CALLBACKCONTEXT_CANDIDATE; // ... means lazyconstraints
if ( CPXcallbacksetfunc(env, lp, contextid, my_callback, inst) ) print_error("CPXcallbacksetfunc() error");

//CPXsetintparam(env, CPX_PARAM_THREADS, 1); // just for debugging

...
CPXmipopt(env,lp); // with the callback installed
//CPXgetx(...); // optimal TSP sol. (or just feasible if time limit...), if any
...

static int CPXPUBLIC my_callback(CPXCALLBACKCONTEXTptr context, CPXLONG contextid, void userhandle){
instance
inst = (instance) userhandle;
double
xstar = (double) malloc(inst->ncols sizeof(double));
double objval = CPX_INFBOUND;
if ( CPXcallbackgetcandidatepoint(context, xstar, 0, inst->ncols-1, &objval) ) print_error("CPXcallbackgetcandidatepoint error");

// get some random information at the node (as an example for the students)

int mythread = -1; CPXcallbackgetinfoint(context, CPXCALLBACKINFO_THREADID, &mythread);
int mynode = -1; CPXcallbackgetinfoint(context, CPXCALLBACKINFO_NODECOUNT, &mynode);
double incumbent = CPX_INFBOUND; CPXcallbackgetinfodbl(context, CPXCALLBACKINFO_BEST_SOL, &incumbent);
//if ( VERBOSE >= 100 ) printf(" ... callback at node %5d thread %2d incumbent %10.2lf, candidate value %10.2lf\n", .....);

...

int nnz = 0; 
... if xstart is infeasible, find a violated cut and store it in the usual Cplex's data structute (rhs, sense, nnz, index and value)

if ( nnz > 0 ) // means that the solution is infeasible and a violated cut has been found
{	
	int izero = 0;		
	if ( CPXcallbackrejectcandidate(context, 1, nnz, &rhs, &sense, &izero, index, value) ) print_error("CPXcallbackrejectcandidate() error"); // reject the solution and adds one cut 

	//if ( CPXcallbackrejectcandidate(context, 0, NULL, NULL, NULL, NULL, NULL, NULL) ) print_error("CPXcallbackrejectcandidate() error"); // just reject the solution without adding cuts (less effective)
}

free(xstar); 
return 0; 

}

Both lazy contraints and usercuts

...
CPXLONG contextid = CPX_CALLBACKCONTEXT_CANDIDATE | CPX_CALLBACKCONTEXT_RELAXATION; // both lazy and usercuts
if ( CPXcallbacksetfunc(env, lp, contextid, my_callback, inst) ) print_error("CPXcallbacksetfunc() error");
...

CPXmipopt(env,lp); 		// with the callback installed

...

/****/
static int CPXPUBLIC my_callback(CPXCALLBACKCONTEXTptr context, CPXLONG contextid, void userhandle )
/****/
{
instance
inst = (instance*) userhandle;

double* xstar = (double*) malloc(inst->ncols * sizeof(double));   
double objval = CPX_INFBOUND; 
if ( contextid == CPX_CALLBACKCONTEXT_CANDIDATE && CPXcallbackgetcandidatepoint(context, xstar, 0, inst->ncols-1, &objval) ) print_error("CPXcallbackgetcandidatepoint error");
if ( contextid == CPX_CALLBACKCONTEXT_RELAXATION && CPXcallbackgetrelaxationpoint(context, xstar, 0, inst->ncols-1, &objval) ) print_error("CPXcallbackgetrelaxationpoint error");

if ( ... )
{
	...
	
	int izero = 0;
	int purgeable = CPX_USECUT_FILTER;
	int local = 0;

	if ( contextid == CPX_CALLBACKCONTEXT_CANDIDATE && CPXcallbackrejectcandidate(context, 1, nnz, &rhs, &sense, &izero, cutind, cutval) ) print_error("CPXcallbackrejectcandidate() error"); // reject the solution and add one cut 
				
	if ( contextid == CPX_CALLBACKCONTEXT_RELAXATION && CPXcallbackaddusercuts(context, 1, nnz, &rhs, &sense, &izero, cutind, cutval,&purgeable, &local) ) print_error("CPXcallbackaddusercuts() error"); // add one violated usercut 
}

...

free(xstar);
return 0; 

}

Alternatively, you can use a simple driver:

/****/
static int CPXPUBLIC my_callback(CPXCALLBACKCONTEXTptr context, CPXLONG contextid, void userhandle )
/****/
{
instance
inst = (instance*) userhandle;
if ( contextid == CPX_CALLBACKCONTEXT_CANDIDATE ) return my_callback_candidate(context, inst);
if ( contextid == CPX_CALLBACKCONTEXT_RELAXATION ) return my_callback_relaxation(context, inst);
print_error("contextid unknownn in my_callback");
return 1;
}


USEFUL PIECES OF CODE

  1. Converting a "successor" TSP solution succ[0..nnodes-1] to a CPLEX solution xheu[0..ncols-1]

double *xheu = (double *) calloc(inst->ncols, sizeof(double));  // all zeros, initially
for ( int i = 0; i < inst->nnodes; i++ ) xheu[xpos(i,succ[i],inst)] = 1.0;
...
free(xheu);
  1. Adding a mipstart solution xheu[0..ncols-1] before calling CPXmipopt (i.e., not from a callback)

int *ind = (int *) malloc(inst->ncols * sizeof(int));
for ( int j = 0; j < inst->ncols; j++ ) ind[j] = j;
int effortlevel = CPX_MIPSTART_NOCHECK;  
int beg = 0; 						
if ( CPXaddmipstarts(env, lp, 1, inst->ncols, &beg, indices, xheu, &effortlevel, NULL) print_error("CPXaddmipstarts() error");	
free(ind);
  1. Posting a heuristic TSP solution xheu[0..ncols-1] within the CPX_CALLBACKCONTEXT_CANDIDATE callback

... starting from the integer xstar, use the patching heuristic + 2-opt to build a heuristic TSP solution xheu[0..ncols-1] of value objheu, and "post it" so CPLEX can use it


int *ind = (int *) malloc(inst->ncols * sizeof(int));
for ( int j = 0; j < inst->ncols; j++ ) ind[j] = j;
if ( CPXcallbackpostheursoln(context, inst->ncols, ind, xheu, objheu, CPXCALLBACKSOLUTION_NOCHECK) ) print_error("CPXcallbackpostheursoln() error");
free(ind);


void add_sec( CPXCENVptr env, CPXLPptr lp, Contextid Cid, const unsigned int nnodes,const int ncomp,const int* comp)